home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / bmplbo / bmplbox.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-12-22  |  15.8 KB  |  475 lines

  1. unit Bmplbox;
  2.  
  3. {
  4.   TBmpListBox & TBmpComboBox components
  5.   *************************************
  6.  
  7.   Freeware by MainSoft sarl.
  8.   Uploaded by Patrick Philippot, CIS: 72561,3532
  9.  
  10.   This unit contains two components implementing an owner-draw Listbox and
  11.   an owner-draw combobox that are able to display a bitmap (glyph) along
  12.   with the item string. They work exactly the same way, so we'll explain
  13.   TBmpListBox only. As you'll see, the code for both component is
  14.   identical, so there's room for optimization by sharing a few routines.
  15.   We didn't make this choice because sharing routines implied passing
  16.   a great number of parameters which would have made the code unclear.
  17.   So, we have merely duplicated the code.
  18.  
  19.   This is a "let's see what we can do with Delphi" package. We may have
  20.   missed some possibilities of optimizing the code. Feel free to enhance
  21.   it and to re-upload. Although the code is rather simple and significantly
  22.   shorter than its BP7 and VC++ counterparts, it took more time to develop,
  23.   due to the lack of a good documentation. In our opinion, Delphi deserves
  24.   a better documentation.
  25.  
  26.   This code is based on information found in TI2793.ASC (a technical note
  27.   from Borland). However, it takes a more sophisticated approach.
  28.  
  29.   The two components have the xxOwnerDrawVariable style which was actually
  30.   not necessary but for an unknown reason, the MeasureItem method is not
  31.   called when we use the xxOwnerDrawFixed style (in that case, the
  32.   WM_MEASUREITEM message is sent only once but it is sent). Since we do
  33.   not have received the VCL source code yet, we can't tell you whether it's
  34.   a bug in the library.
  35.  
  36.   *********************
  37.  
  38.   TBmpListBox derives from TListBox and adds the capability of displaying
  39.   a bitmap on the left of the item string. Each item in the listbox can have
  40.   a different bitmap (glyph). Both the bitmap and the text string are
  41.   automatically centered vertically. So they can be of any height (within a
  42.   reasonable range).
  43.  
  44.   In order to limit system resource consumption, TBmpListBox assumes that
  45.   all glyphs are contained within a single bitmap strip and that they all have
  46.   the same width. This way, each glyph can be indexed. The bitmap strip is a
  47.   property of TBmpListBox and is initially empty.
  48.  
  49.   The index of the glyph associated with a particular listbox item is stored
  50.   in the HiWord of Items.Object[item_index]. The LoWord can be used by the
  51.   application. The best way to make this association is to use the AddObject
  52.   method and to do some typecasting. See sample program.
  53.  
  54.   This approach has a drawback regarding the general philosophy of developing
  55.   Delphi Components. Since there's no way for TBmpListBox to determine the
  56.   width of a single glyph in the bitmap strip, no bitmap will be displayed
  57.   until the user defines a positive value for the BmpItemWidth property.
  58.   Also, there is no "default bitmap".
  59.  
  60.   If no TBitmap has been assigned to BitmapStrip or if BmpItemWidth is null or
  61.   if the assigned bitmap is empty, TBmpListBox will behave as a standard
  62.   listbox.
  63.  
  64.   New properties:
  65.   _______________
  66.  
  67.   All these properties can be changed dynamically at run time (although
  68.   this will happen very rarely).
  69.  
  70.  
  71.   BitmapStrip         A TBitmap that must be supplied by the application.
  72.              BitmapStrip defaults to nil (none).
  73.  
  74.              Once you have assigned a TBitmap to BitmapStrip, you
  75.              can Destroy the source bitmap. SetBitmapStrip uses the
  76.              Assign method to copy the bitmap data.
  77.  
  78.   BmpItemWidth         The width, in pixels, of one single glyph in the bitmap
  79.              strip. Both BitmapStrip and BmpItemWidth must be valid
  80.              in order to display an associated bitmap with each item.
  81.              BmpItemWidth defaults to 0.
  82.  
  83.   Leftmargin         The space in pixels left between the left side of the
  84.              listbox and the left side of the glyph AND between the
  85.              right side of the glyph and the beginning of the text
  86.              string. This value is ignored if BmpItemWidth and/or
  87.              BitmapStrip are not valid. Leftmargin defaults to 4.
  88.  
  89.   TopAndBottomMargin The additional space in pixels left at the bottom AND
  90.              at the top of the item rectangle. TopAndBottomMargin
  91.              defaults to 3.
  92.  
  93.   TransparentColor   This TColor defines which color in the glyph will be
  94.              made transparent when displaying the glyph on the
  95.              item's rectangle background. TransparentColor defaults
  96.              to clGray.
  97.  
  98.   *********************
  99.  
  100.   You are granted the right to use and peruse this code in your applications
  101.   without notifying MainSoft. However, this code can't be published without
  102.   written permission of MainSoft.
  103.  
  104.   Have fun!
  105.  
  106.  
  107.   A few words about MainSoft:
  108.   ***************************
  109.  
  110.   MainSoft sarl is a french company created by Patrick Philippot, a
  111.   former IBM engineer. MainSoft specializes in training (VB, VC++,
  112.   OLE2, ODBC, ...), consulting and development for Windows and Windows
  113.   NT. We also have a good experience in software localization. Our
  114.   flagship product is a shareware programming editor: E! for Windows.
  115.  
  116.   E! is the most powerful shareware editor available. Syntax Highlighting
  117.   for any language (user configurable), function tagging and many other
  118.   original features make this product unique. It is as powerful as (or even
  119.   more powerful than) many shrink-wrapped text editors but at a fraction of
  120.   the price.
  121.  
  122.  *************************************************************************
  123.   As it supports the Borland Pascal compiler, E! also fully supports the
  124.   Delphi command line compiler. You can transparently compile and jump to
  125.   the syntax errors in the source file without even seeing DCC.EXE running.
  126.  *************************************************************************
  127.  
  128.   You can download E! from many Compuserve libraries (PCAPP, WINSHARE,
  129.   WINSDK, WUGNET,...). Look for EWARC2.EXE. Available patches are
  130.   always uploaded as EWPxxx.ZIP.
  131.  
  132.   Feel free to drop a message to Patrick Philippot [72561,3532] if you
  133.   need any information.
  134.  
  135.      MainSoft sarl
  136.      15, avenue des Pres Pierre
  137.      91210 Draveil
  138.      France
  139.      tel/fax: +33 1 69 40 94 85
  140.      CIS: 72561,3532
  141.      INTERNET: 72561.3532@compuserve.com
  142.  
  143.   Currently, the distribution of E! is managed in the USA and Canada by
  144.  
  145.        HomeBrew Software
  146.        807 Davis Street
  147.        Suite E
  148.        Vacaville, CA 95687
  149.        (707) 451-9653  Voice
  150.        (707) 451-2500  FAX
  151.  
  152.   and by Juergen Egeling Computer (for other countries with exception
  153.   of France)
  154.  
  155.        Juergen Egeling Computer
  156.        Werderstr. 41, 76137 Karlsruhe, Germany.
  157.        Tel: +49 (0721) 373832 / Fax: +49 (0721) 373842
  158.        email: fft@jecalpha.ka.sub.org
  159.  
  160.  
  161.   Draveil, France, 06-07-95
  162. }
  163.  
  164. interface
  165.  
  166. uses
  167.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  168.   Forms, Dialogs, StdCtrls;
  169.  
  170. type
  171.  
  172.   TBmpListBox = class(TListBox)
  173.   private
  174.     { Private declarations }
  175.     FBitmapStrip    : TBitmap;
  176.     FBmpItemWidth    : integer;
  177.     FLeftMargin     : integer;
  178.     FTopAndBottomMargin : integer;
  179.     FTransparentColor    : TColor;
  180.     bOkToDraw        : boolean;
  181.     yBmpOffset        : integer;
  182.  
  183.   protected
  184.     { Protected declarations }
  185.     procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
  186.     procedure MeasureItem(Index: Integer; var Height: Integer); override;
  187.  
  188.   public
  189.     { Public declarations }
  190.     constructor Create(AOwner : TComponent); override;
  191.     destructor    Destroy; override;
  192.     procedure    SetBitmapStrip(ABitmapStrip : TBitmap);
  193.     procedure    SetBmpItemWidth(NewWidth : integer);
  194.     procedure    SetLeftMargin(NewMargin : integer);
  195.     procedure    SetTopAndBottomMargin(NewMargin : integer);
  196.     procedure    SetTransparentColor(NewColor : TColor);
  197.     procedure    CheckContext;
  198.  
  199.   published
  200.     { Published declarations }
  201.     property BitmapStrip : TBitmap read FBitmapStrip write SetBitmapStrip;
  202.     property BmpItemWidth : integer read FBmpItemWidth write SetBmpItemWidth default 0;
  203.     property LeftMargin : integer read FLeftMargin write SetLeftMargin default 4;
  204.     property TopAndBottomMargin : integer read FTopAndBottomMargin write SetTopAndBottomMargin default 3;
  205.     property TransparentColor : TColor read FTransparentColor write SetTransparentColor default clGray;
  206.   end;
  207.  
  208.   TBmpComboBox = class(TComboBox)
  209.   private
  210.     { Private declarations }
  211.     FBitmapStrip    : TBitmap;
  212.     FBmpItemWidth    : integer;
  213.     FLeftMargin     : integer;
  214.     FTopAndBottomMargin : integer;
  215.     FTransparentColor    : TColor;
  216.     bOkToDraw        : boolean;
  217.     yBmpOffset        : integer;
  218.  
  219.   protected
  220.     { Protected declarations }
  221.     procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
  222.     procedure MeasureItem(Index: Integer; var Height: Integer); override;
  223.  
  224.   public
  225.     { Public declarations }
  226.     constructor Create(AOwner : TComponent); override;
  227.     destructor    Destroy; override;
  228.     procedure    SetBitmapStrip(ABitmapStrip : TBitmap);
  229.     procedure    SetBmpItemWidth(NewWidth : integer);
  230.     procedure    SetLeftMargin(NewMargin : integer);
  231.     procedure    SetTopAndBottomMargin(NewMargin : integer);
  232.     procedure    SetTransparentColor(NewColor : TColor);
  233.     procedure    CheckContext;
  234.  
  235.   published
  236.     { Published declarations }
  237.     property BitmapStrip : TBitmap read FBitmapStrip write SetBitmapStrip;
  238.     property BmpItemWidth : integer read FBmpItemWidth write SetBmpItemWidth default 0;
  239.     property LeftMargin : integer read FLeftMargin write SetLeftMargin default 4;
  240.     property TopAndBottomMargin : integer read FTopAndBottomMargin write SetTopAndBottomMargin default 3;
  241.     property TransparentColor : TColor read FTransparentColor write SetTransparentColor default clGray;
  242.   end;
  243.  
  244. procedure Register;
  245.  
  246.  
  247. implementation
  248.  
  249.  
  250. {-TBmpListBox}
  251.  
  252. constructor TBmpListBox.Create(AOwner : TComponent);
  253. begin
  254.   inherited Create(AOwner);
  255.   FBitmapStrip := TBitmap.Create;
  256.   FBmpItemWidth := 0;
  257.   yBmpOffset := 0;
  258.   FLeftMargin := 4;
  259.   FTopAndBottomMargin := 3;
  260.   FTransparentColor := clGray;
  261.   Style := lbOwnerDrawVariable;
  262.  
  263.   {-We should be able to use the lbOwnerDrawFixed style but, strangely
  264.    enough, MeasureItem is never called in that case. Normally, when the
  265.    lbOwnerDrawFixed style is used, the WM_MEASUREITEM message is
  266.    sent once and only once. Since I don't have received the VCL source
  267.    code yet, I cannot explain this behavior but it looks like a bug.}
  268.  
  269.   bOkToDraw := false;
  270. end;
  271.  
  272. destructor TBmpListBox.Destroy;
  273. begin
  274.   if Assigned(FBitmapStrip) then
  275.     FBitmapStrip.Destroy;
  276.   inherited Destroy;
  277. end;
  278.  
  279. procedure TBmpListBox.CheckContext;
  280. begin
  281.  {-Verify that critical properties have been correctly setup}
  282.   bOkToDraw := (FBmpItemWidth > 0) and Assigned(FBitmapStrip) and not FBitmapStrip.Empty;
  283. end;
  284.  
  285. procedure TBmpListBox.SetBitmapStrip(ABitmapStrip : TBitmap);
  286. begin
  287.  {-Copy data from source bitmap}
  288.   FBitmapStrip.Assign(ABitmapStrip);
  289.   CheckContext;
  290.   Invalidate;
  291. end;
  292.  
  293. procedure TBmpListBox.SetBmpItemWidth(NewWidth : integer);
  294. begin
  295.   FBmpItemWidth := NewWidth;
  296.   CheckContext;
  297.   Invalidate;
  298. end;
  299.  
  300. procedure TBmpListBox.SetLeftMargin(NewMargin : integer);
  301. begin
  302.   FLeftMargin := NewMargin;
  303.   Invalidate;
  304. end;
  305.  
  306. procedure TBmpListBox.SetTransparentColor(NewColor : TColor);
  307. begin
  308.   FTransparentColor := NewColor;
  309.   Invalidate;
  310. end;
  311.  
  312. procedure TBmpListBox.SetTopAndBottomMargin(NewMargin : integer);
  313. begin
  314.   FTopAndBottomMargin := NewMargin;
  315.   Invalidate;
  316. end;
  317.  
  318. procedure TBmpListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  319. var
  320.   OutStr   : PChar;
  321.   len       : word;
  322. begin
  323.   with Canvas do begin
  324.     FillRect(Rect);
  325.    {-Check critical properties and validity of glyph index}
  326.     if bOkToDraw and ((BmpItemWidth * HiWord(longint(Items.Objects[Index]))) < FBitmapStrip.Width) then
  327.       BrushCopy(Bounds(Rect.left + FLeftMargin,
  328.                Rect.top + yBmpOffset,
  329.                FBmpItemWidth,
  330.                FBitmapStrip.Height),
  331.         FBitmapStrip,
  332.         Bounds(BmpItemWidth * HiWord(longint(Items.Objects[Index])),
  333.                0,
  334.                FBmpItemWidth,
  335.                FBitmapStrip.Height),
  336.         FTransparentColor);
  337.    {-If we're not "OKToDraw", the LeftMargin property is ignored}
  338.    {-We use the DrawText API which is more accurate than Canvas.TextOut}
  339.     Rect.left := Rect.left + BmpItemWidth + (FLeftMargin * 2 * Ord(bOkToDraw));
  340.     len := Length(Items[index]);
  341.     GetMem(OutStr, len + 1);
  342.     StrPCopy(OutStr, Items[index]);
  343.     DrawText(Handle, OutStr, len, Rect, dt_Left or dt_VCenter or dt_SingleLine);
  344.     FreeMem(OutStr, len + 1);
  345.   end;
  346. end;
  347.  
  348. procedure TBmpListBox.MeasureItem(Index: Integer; var Height: Integer);
  349. var
  350.   TxtHeight : integer;
  351. begin
  352.   if bOkToDraw then begin
  353.     TxtHeight := Abs(Font.Height);
  354.    {- When we receive the WM_MEASUREITEM message, the font used for the
  355.      Control has not been yet determined by Windows. Using Canvas.TextHeight
  356.      would return a wrong value. So, we MUST use the Font property to
  357.      retrieve the font height.}
  358.     if TxtHeight > FBitmapStrip.Height then
  359.       Height := TxtHeight
  360.     else
  361.       Height := FBitmapStrip.Height;
  362.     Inc(Height, FTopAndBottomMargin * 2);
  363.     yBmpOffset := (Height - FBitmapStrip.Height) div 2;
  364.   end;
  365. end;
  366.  
  367.  
  368. {-TBmpComboBox - Identical to TBmpListBox}
  369.  
  370. constructor TBmpComboBox.Create(AOwner : TComponent);
  371. begin
  372.   inherited Create(AOwner);
  373.   FBitmapStrip := TBitmap.Create;
  374.   FBmpItemWidth := 0;
  375.   yBmpOffset := 0;
  376.   FLeftMargin := 4;
  377.   FTopAndBottomMargin := 3;
  378.   FTransparentColor := clGray;
  379.   Style := csOwnerDrawVariable;
  380.   bOkToDraw := false;
  381. end;
  382.  
  383. destructor TBmpComboBox.Destroy;
  384. begin
  385.   if Assigned(FBitmapStrip) then
  386.     FBitmapStrip.Destroy;
  387.   inherited Destroy;
  388. end;
  389.  
  390. procedure TBmpComboBox.CheckContext;
  391. begin
  392.   bOkToDraw := (FBmpItemWidth > 0) and Assigned(FBitmapStrip) and not FBitmapStrip.Empty;
  393. end;
  394.  
  395. procedure TBmpComboBox.SetBitmapStrip(ABitmapStrip : TBitmap);
  396. begin
  397.   FBitmapStrip.Assign(ABitmapStrip);
  398.   CheckContext;
  399. end;
  400.  
  401. procedure TBmpComboBox.SetBmpItemWidth(NewWidth : integer);
  402. begin
  403.   FBmpItemWidth := NewWidth;
  404.   CheckContext;
  405. end;
  406.  
  407. procedure TBmpComboBox.SetLeftMargin(NewMargin : integer);
  408. begin
  409.   FLeftMargin := NewMargin;
  410.   Invalidate;
  411. end;
  412.  
  413. procedure TBmpComboBox.SetTransparentColor(NewColor : TColor);
  414. begin
  415.   FTransparentColor := NewColor;
  416.   Invalidate;
  417. end;
  418.  
  419. procedure TBmpComboBox.SetTopAndBottomMargin(NewMargin : integer);
  420. begin
  421.   FTopAndBottomMargin := NewMargin;
  422.   Invalidate;
  423. end;
  424.  
  425. procedure TBmpComboBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  426. var
  427.   OutStr   : PChar;
  428.   len       : word;
  429. begin
  430.   with Canvas do begin
  431.     FillRect(Rect);
  432.     if bOkToDraw and ((BmpItemWidth * HiWord(longint(Items.Objects[Index]))) < FBitmapStrip.Width) then
  433.       BrushCopy(Bounds(Rect.Left + FLeftMargin,
  434.                Rect.Top + yBmpOffset,
  435.                FBmpItemWidth,
  436.                FBitmapStrip.Height),
  437.         FBitmapStrip,
  438.         Bounds(BmpItemWidth * HiWord(longint(Items.Objects[Index])),
  439.                0,
  440.                FBmpItemWidth,
  441.                FBitmapStrip.Height),
  442.         FTransparentColor);
  443.     Rect.left := Rect.left + BmpItemWidth + (FLeftMargin * 2 * Ord(bOkToDraw));
  444.     len := Length(Items[index]);
  445.     GetMem(OutStr, len + 1);
  446.     StrPCopy(OutStr, Items[index]);
  447.     DrawText(Handle, OutStr, len, Rect, dt_Left or dt_VCenter or dt_SingleLine);
  448.     FreeMem(OutStr, len + 1);
  449.   end;
  450. end;
  451.  
  452. procedure TBmpComboBox.MeasureItem(Index: Integer; var Height: Integer);
  453. var
  454.   TxtHeight : integer;
  455. begin
  456.   if bOkToDraw then begin
  457.     TxtHeight := Abs(Font.Height);
  458.     if TxtHeight > FBitmapStrip.Height then
  459.       Height := TxtHeight
  460.     else
  461.       Height := FBitmapStrip.Height;
  462.     Inc(Height, FTopAndBottomMargin * 2);
  463.     yBmpOffset := (Height - FBitmapStrip.Height) div 2;
  464.   end;
  465. end;
  466.  
  467.  
  468. {-register both components}
  469. procedure Register;
  470. begin
  471.   RegisterComponents('Additional', [TBmpListBox, TBmpComboBox]);
  472. end;
  473.  
  474. end.
  475.